Aprende a implementar Error Boundaries en React con hooks para manejar elegantemente los errores de carga de recursos, mejorando la experiencia de usuario y la estabilidad de la aplicaci贸n.
Carga Robusta de Recursos en React: Dominando los Error Boundaries con Hooks
En las aplicaciones web modernas, la carga de recursos de forma as铆ncrona es una pr谩ctica com煤n. Ya sea obteniendo datos de una API, cargando im谩genes o importando m贸dulos, manejar los posibles errores durante la carga de recursos es crucial para una experiencia de usuario fluida. Los Error Boundaries de React proporcionan un mecanismo para capturar errores de JavaScript en cualquier parte de su 谩rbol de componentes hijos, registrar esos errores y mostrar una UI de respaldo en lugar de bloquear toda la aplicaci贸n. Este art铆culo explora c贸mo usar eficazmente los Error Boundaries junto con los Hooks de React para gestionar los errores de carga de recursos.
Entendiendo los Error Boundaries
Antes de React 16, los errores de JavaScript no controlados durante el renderizado de un componente corromp铆an el estado interno de React y causaban errores cr铆pticos en renderizados posteriores. Los Error Boundaries solucionan esto actuando como bloques "catch-all" para los errores que ocurren en sus componentes hijos. Son componentes de React que implementan uno o ambos de los siguientes m茅todos de ciclo de vida:
static getDerivedStateFromError(error): Este m茅todo est谩tico se invoca despu茅s de que un componente descendiente haya lanzado un error. Recibe el error que fue lanzado como argumento y devuelve un valor para actualizar el estado del componente.componentDidCatch(error, info): Este m茅todo de ciclo de vida se invoca despu茅s de que un componente descendiente haya lanzado un error. Recibe el error que fue lanzado como argumento, as铆 como un objeto que contiene informaci贸n sobre qu茅 componente lanz贸 el error. Puedes usarlo para registrar informaci贸n del error.
Es importante destacar que los Error Boundaries solo capturan errores en la fase de renderizado, en los m茅todos de ciclo de vida y en los constructores de todo el 谩rbol debajo de ellos. No capturan errores para:
- Manejadores de eventos (aprende m谩s en la secci贸n de abajo)
- C贸digo as铆ncrono (p. ej., callbacks de
setTimeoutorequestAnimationFrame) - Renderizado del lado del servidor
- Errores lanzados en el propio Error Boundary (en lugar de en sus hijos)
Error Boundaries y Hooks de React: Una Combinaci贸n Poderosa
Aunque los componentes de clase se usaban tradicionalmente para implementar Error Boundaries, los Hooks de React ofrecen un enfoque m谩s conciso y funcional. Podemos crear un hook reutilizable useErrorBoundary que encapsule la l贸gica de manejo de errores y proporcione una forma conveniente de envolver componentes que puedan lanzar errores durante la carga de recursos.
Creando un Hook useErrorBoundary Personalizado
Aqu铆 hay un ejemplo de un hook useErrorBoundary:
import { useState, useCallback } from 'react';
function useErrorBoundary() {
const [error, setError] = useState(null);
const resetError = useCallback(() => {
setError(null);
}, []);
const captureError = useCallback((e) => {
setError(e);
}, []);
const ErrorBoundary = useCallback(({ children, fallback }) => {
if (error) {
return fallback ? fallback : Ocurri贸 un error: {error.message || String(error)};
}
return children;
}, [error]);
return { ErrorBoundary, captureError, error, resetError };
}
export default useErrorBoundary;
Explicaci贸n:
useState: UsamosuseStatepara gestionar el estado del error. Inicialmente establece el error ennull.useCallback: UsamosuseCallbackpara memoizar las funcionesresetErrorycaptureError. Esta es una buena pr谩ctica para evitar re-renderizados innecesarios si estas funciones se pasan como props.- Componente
ErrorBoundary: Este es un componente funcional creado conuseCallbackque tomachildreny un prop opcionalfallback. Si existe un error en el estado, renderiza el componentefallbackproporcionado o un mensaje de error por defecto. De lo contrario, renderiza los hijos. Esto act煤a como nuestro Error Boundary. El array de dependencias `[error]` asegura que se vuelva a renderizar cuando el estado de `error` cambie. - Funci贸n
captureError: Esta funci贸n se usa para establecer el estado del error. La llamar谩s dentro de un bloquetry...catchal cargar recursos. - Funci贸n
resetError: Esta funci贸n limpia el estado del error, permitiendo que el componente vuelva a renderizar a sus hijos (potencialmente reintentando la carga del recurso).
Implementando la Carga de Recursos con Manejo de Errores
Ahora, veamos c贸mo usar este hook para manejar errores de carga de recursos. Considera un componente que obtiene datos de usuario de una API:
import React, { useState, useEffect } from 'react';
import useErrorBoundary from './useErrorBoundary';
function UserProfile({ userId }) {
const [user, setUser] = useState(null);
const { ErrorBoundary, captureError, error, resetError } = useErrorBoundary();
useEffect(() => {
const fetchData = async () => {
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`隆Error HTTP! estado: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (e) {
captureError(e);
}
};
fetchData();
}, [userId, captureError]);
if (error) {
return (
No se pudieron cargar los datos del usuario. {user.name}
Email: {user.email}
{/* Otros detalles del usuario */}Explicaci贸n:
- Importamos el hook
useErrorBoundary. - Llamamos al hook para obtener el componente
ErrorBoundary, la funci贸ncaptureError, el estadoerrory la funci贸nresetError. - Dentro del hook
useEffect, envolvemos la llamada a la API en un bloquetry...catch. - Si ocurre un error durante la llamada a la API, llamamos a
captureError(e)para establecer el estado del error. - Si el estado
errorest谩 establecido, renderizamos el componenteErrorBoundary. Proporcionamos un propfallbackpersonalizado que muestra un mensaje de error y un bot贸n de "Reintentar". Al hacer clic en el bot贸n se llama aresetErrorpara limpiar el estado del error, lo que desencadena un nuevo renderizado y otro intento de obtener los datos. - Si no ocurri贸 ning煤n error y los datos del usuario se cargaron, renderizamos los detalles del perfil del usuario.
Manejando Diferentes Tipos de Errores de Carga de Recursos
Diferentes tipos de errores de carga de recursos pueden requerir diferentes estrategias de manejo. Aqu铆 hay algunos escenarios comunes y c贸mo abordarlos:
Errores de Red
Los errores de red ocurren cuando el cliente no puede conectarse al servidor (p. ej., debido a una interrupci贸n de la red o una ca铆da del servidor). El ejemplo anterior ya maneja errores de red b谩sicos usando `response.ok`. Es posible que desees agregar una detecci贸n de errores m谩s sofisticada, por ejemplo:
//Dentro de la funci贸n fetchData
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
// Considerar agregar manejo espec铆fico de c贸digos de error
if (response.status === 404) {
throw new Error("Usuario no encontrado");
} else if (response.status >= 500) {
throw new Error("Error del servidor. Por favor, int茅ntalo de nuevo m谩s tarde.");
} else {
throw new Error(`隆Error HTTP! estado: ${response.status}`);
}
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error.message === 'Failed to fetch') {
// Probablemente un error de red
captureError(new Error('Error de red. Por favor, revisa tu conexi贸n a internet.'));
} else {
captureError(error);
}
}
En este caso, puedes mostrar un mensaje al usuario indicando que hay un problema de conectividad de red y sugerir que revisen su conexi贸n a internet.
Errores de API
Los errores de API ocurren cuando el servidor devuelve una respuesta de error (p. ej., un 400 Bad Request o un 500 Internal Server Error). Como se muestra arriba, puedes verificar `response.status` y manejar estos errores apropiadamente.
Errores de Parseo de Datos
Los errores de parseo de datos ocurren cuando la respuesta del servidor no est谩 en el formato esperado y no se puede analizar (p. ej., JSON inv谩lido). Puedes manejar estos errores envolviendo la llamada response.json() en un bloque try...catch:
//Dentro de la funci贸n fetchData
try {
const response = await fetch(`https://api.example.com/users/${userId}`);
if (!response.ok) {
throw new Error(`隆Error HTTP! estado: ${response.status}`);
}
const data = await response.json();
setUser(data);
} catch (error) {
if (error instanceof SyntaxError) {
captureError(new Error('No se pudieron analizar los datos del servidor.'));
} else {
captureError(error);
}
}
Errores de Carga de Im谩genes
Para la carga de im谩genes, puedes usar el manejador de eventos onError en la etiqueta <img>:
function MyImage({ src, alt }) {
const { ErrorBoundary, captureError } = useErrorBoundary();
const [imageLoaded, setImageLoaded] = useState(false);
const handleImageLoad = () => {
setImageLoaded(true);
};
const handleImageError = (e) => {
captureError(new Error(`No se pudo cargar la imagen: ${src}`));
};
return (
No se pudo cargar la imagen.